// mongoose setup
require( '../../../config' );
var fs           = require('fs'),
    Promise     = require('bluebird'),
    assert       = require('assert'),
    nock         = require('nock'),
    mongoose = require('mongoose'),
    helper      = require('../../../helper'),
    weixin       = require('../../../../app/components/backend/service/weixin'),
    qrcode      = require('../../../../app/components/backend/service/qrcode'),
    weixinDao = require('../../../../app/components/backend/db/daos/weixinuser'),
    weixinModel = require('../../../../app/components/backend/db/models/weixinuser'),
    accessTokenModel = require('../../../../app/components/backend/db/models/accesstoken'),
    pointhistoryModel = require('../../../../app/components/backend/db/models/pointhistory'),
    sinon   = require('sinon'),  sinonSandbox,
    util = require('../../../../app/components/backend/util'),
    config = require('../../../../app/components/backend/util').config,
    access_token = 'BYEsg8wnKAM0d_7uxOjq7E7LgTeCPBw-FQa_-NVUviAMjMSKFD-85Z7rfrJa9vLX4-g3IpDJeKh6EpAp231gofjGokYc23nC9oQ4FX_k2Cs',
    user_info    = {
      "subscribe"     : 1,
      "openid"        : "oNaHXsspWthiBKBda-E5C_OBP_ao",
      "nickname"      : "曾健",
      "sex"           : 1,
      "language"      : "zh_CN",
      "city"          : "大连",
      "province"      : "辽宁",
      "country"       : "中国",
      "headimgurl"    : "http:\/\/wx.qlogo.cn\/mmopen\/7y4xnsv09YZOoic8yb10tqx5rnflfUEFFbMD44F8yqGrDIcAutL4hucrDLeXCN98QQFPkyuNWgj4CpYMMwsygMnJDuplOjF5u\/0",
      "subscribe_time": 1449009999,
      "remark"        : "",
      "groupid"       : 0
    },
    mockQrcode = 'https://mp.weixin.qq.com/cgi-bin/showqrcode?ticket=gQH47joAAAAAAAAAASxodHRwOi8vd2VpeGluLnFxLmNvbS9xL2taZ2Z3TVRtNzJXV1Brb3ZhYmJJAAIEZ23sUwMEmm3sUw==';

// 下面的值应该与xml数据文件中的对应值一致
var testingOpenid = 'oNaHXsspWthiBKBda-E5C_OBP_ao',
    testingEmail = 'neil.zeng@tsinghua.edu.cn';

var mockGetWeixinUser = function(isAuth) {
  return new Promise(function(resolve, reject) {

    // 模拟公众账号未认证
    if (isAuth !== true) {
      user_info.errcode = '48001';
    }

    // 模拟请求, nock会把执行过的interceptor从list中remove掉; 所以对于一个url的多次请求要多次设置
    nock('https://api.weixin.qq.com').get('/cgi-bin/user/info').query({

      // Mock 用户信息
      access_token: access_token,
      openid      : testingOpenid
    }).reply(200, user_info);
    resolve();
  });
};

describe('app/components/backend/service/weixin', function () {
  describe('#processMessage().关注处理', function () {

    // 测试前置处理
    beforeEach(function () {
      return mockGetWeixinUser(true).then(helper.makeTokenNeverOutofDate).then(function() {
        // 初始化sinon
        sinonSandbox = sinon.sandbox.create();
        return Promise.resolve();
      });
    });

    // 测试后置处理
    afterEach(function() {
      return new Promise(function (resolve, reject) {

        // 复位
        if (sinonSandbox) sinonSandbox.restore();

        // 清空weixinusers collection
        weixinModel.remove({}, function(weixinErr) {
          if (weixinErr) throw new Error(weixinErr);

          // 清空accessToken collection
          accessTokenModel.remove({}, function (tokenErr) {
            if (tokenErr) throw new Error(tokenErr);
            resolve();
          });
        });
      });
    });
    var subscribeData = fs.readFileSync('test/components/backend/service/subscribe_data.xml').toString();
    it('[processMessage.subscribe.Nor.01].用户正常(非扫描二维码)关注公众号, 应保存微信元信息', function () {
      sinonSandbox.stub(qrcode, "getQrcode").returns(Promise.resolve(mockQrcode));
      return weixin.processMessage(subscribeData).then(function (data, error) {
        if (data) {
          assert.equal(data.ok , 1, '应该更新/插入成功返回1, 但是返回值是' + data.ok);
          assert.equal(data.n, 1, '应该只更新/插入了一条 ,但是返回值是' + data.n);
          assert.equal(data.action_type, 'subscribe');
          assert.ok(data.msg.indexOf('感谢关注科研共济会') > 0);
          return weixinDao.findByOpenId(testingOpenid).then(function(user) {
            if (user) {
              assert.ok(parseInt('01010101',16) < user.promotion_code < parseInt('FFFFFFFF',16));
              assert.equal(mockQrcode, user.qrcode);
              return Promise.resolve();
            } else {
              return Promise.reject();
            }
          });
        }
        if (error) {
          assert.ok(false, '更新/插入失败');
          return Promise.reject();
        }
      });
    });

    it('[processMessage.subscribe.Nor.02].关注公众号 -> 取消关注 -> 关注, 只更新subscribe_time, 其他不变', function () {

      sinonSandbox.stub(qrcode, "getQrcode").returns(Promise.resolve(mockQrcode));

      // 第一次关注
      return weixin.processMessage(subscribeData).then(function () {

        // 再次关注，只有subsribe_time被改变
        // nock会把执行过的interceptor从list中remove掉; 所以对于一个url的多次请求要多次设置
        return mockGetWeixinUser(true).then(function() {
          return weixin.processMessage(subscribeData).then(function(){
            return weixinDao.findByOpenId(testingOpenid).then(function(data) {
              assert.ok(0 <= Date.now() - data.subscribe_time < 10);
              assert.equal(data.due_date, 0);
              assert.equal(data.register_date, 0);
              return Promise.resolve();
            });
          });
        });
      });
    });
  });

  describe('#processMessage().通过二维码关注', function () {
    beforeEach(function () {
      sinonSandbox = sinon.sandbox.create();
    });
    afterEach(function () {

      // 清空weixinusers collection
      weixinModel.remove({}, function(err) {
        if (err) throw new Error(err);
        console.log('\n>>>>>>>>>> weixin collection removed');
      });

      pointhistoryModel.remove({}, function(err) {
        if (err) throw new Error(err);
        console.log('\n>>>>>>>>>> pointhistory collection removed');
      });

      // 复位
      if (sinonSandbox) sinonSandbox.restore();
    });
    var subscribeByQrcodeData = fs.readFileSync('test/components/backend/service/subscribe_by_scan.xml').toString();
    var defaultUserData = JSON.parse(fs.readFileSync('test/components/backend/service/default_user_data.json', 'utf8'));

    defaultUserData.promotion_code = util.generatePromotionCode(defaultUserData.openid);
    it('[processMessage.subscribeByQrcode.Nor.01].用户扫描二维码关注公众号, 应保存微信元信息', function () {
      sinonSandbox.stub(util.weixin, "getUserInfo").returns(Promise.resolve({
        "subscribe"     : 1,
        "openid"        : "oCxVxs7eTWNcwP2QyGqlDXyZpbVE",
        "nickname"      : "曾大点",
        "sex"           : 1,
        "language"      : "zh_CN",
        "city"          : "大连",
        "province"      : "辽宁",
        "country"       : "中国",
        "headimgurl"    : "http:\/\/wx.qlogo.cn\/mmopen\/7y4xnsv09YZOoic8yb10tqx5rnflfUEFFbMD44F8yqGrDIcAutL4hucrDLeXCN98QQFPkyuNWgj4CpYMMwsygMnJDuplOjF5u\/0",
        "subscribe_time": 1445847409,
        "remark"        : "",
        "groupid"       : 0
      }));

      sinonSandbox.stub(qrcode, "getQrcode").returns(Promise.resolve(
        'https://mp.weixin.qq.com/cgi-bin/showqrcode?ticket=gQH47joAAAAAAAAAASxodHRwOi8vd2VpeGluLnFxLmNvbS9xL2taZ2Z3TVRtNzJXV1Brb3ZhYmJJAAIEZ23sUwMEmm3sUw=='
      ));

      var spyEmit = sinonSandbox.spy(util.sandbox, 'emit');
      return weixinDao.findOneAndUpdate(defaultUserData, defaultUserData).then(function() {
        return weixin.processMessage(subscribeByQrcodeData).then(function (msg) {
          assert.equal(msg.ok , 1, '应该更新/插入成功返回1, 但是返回值是' + msg.ok);
          assert.equal(msg.n, 1, '应该只更新/插入了一条 ,但是返回值是' + msg.n);
          assert.equal(msg.action_type, 'subscribe_via_qrcode');
          assert.ok(msg.msg.indexOf('感谢关注科研共济会') > 0);
          return weixinDao.findByOpenId('oCxVxs7eTWNcwP2QyGqlDXyZpbVE').then(function (userInfo) {
            assert.equal(userInfo.reference_code, defaultUserData.promotion_code, '推荐者code和预期不一致'); // 推荐者code应该被保存
          });
        }).delay(500)
          .then(function () {
            assert.equal(spyEmit.firstCall.args[0], util.sandbox.MSG.LOG_POINTHISTORY);
            assert.equal(spyEmit.firstCall.args[1].triggered_by, 'oCxVxs7eTWNcwP2QyGqlDXyZpbVE');
            assert.equal(spyEmit.firstCall.args[1].triggered_by_name, '曾大点');
            assert.equal(spyEmit.firstCall.args[1].action_type, 'subscribe_via_qrcode');
            assert.equal(spyEmit.firstCall.args[1].awarded_to, 'oNaHXsspWthiBKBda-E5C_OBP_ao');
            assert.equal(spyEmit.firstCall.args[1].awarded_to_name, '曾健');
            assert.equal(spyEmit.firstCall.args[1].point, util.config.award_subscribe_qrcode);
            assert.equal(spyEmit.secondCall.args[0], util.sandbox.MSG.EXCH_POINT_TO_VPN_TIME);
            assert.equal(spyEmit.secondCall.args[1].openid, 'oNaHXsspWthiBKBda-E5C_OBP_ao');
            assert.equal(spyEmit.secondCall.args[1].dates, 999);
          });
      });
    });
  });

  describe('#processMessage().注册处理', function () {

    // 测试前置处理
    beforeEach(function () {
      return mockGetWeixinUser(true).then(helper.makeTokenNeverOutofDate).then(function() {
        // 初始化sinon
        sinonSandbox = sinon.sandbox.create();
        sinonSandbox.stub(qrcode, "getQrcode").returns(Promise.resolve(mockQrcode));
        return Promise.resolve();
      });
    });

    // 测试后置处理
    afterEach(function() {

      // 复位
      if (sinonSandbox) sinonSandbox.restore();

      // 清空weixinusers collection
      weixinModel.remove({}, function(err) {
        if (err) throw new Error(err);
        weixinModel.collection.dropAllIndexes(function (err, results) {
          // Handle errors
        });
      });
    });

    // 注意:确认XML中的ToUserName, FromUserName在`关注`和`注册`两个模拟数据中一致
    var registerData = fs.readFileSync('test/components/backend/service/register_data.xml').toString(),
        subscribeData = fs.readFileSync('test/components/backend/service/subscribe_data.xml').toString();

    it('[processMessage.register.Nor.04].注册, email格式不对, 提示用户使用正确的edu邮箱完成注册', function () {

      // 注册
      var incurrectEmailRegData = registerData.replace(testingEmail, 'neil.zeng@tsinghua.edx.cn');

      // 先关注
      return weixin.processMessage(subscribeData).then(function() {
        return weixin.processMessage(incurrectEmailRegData).then(function(data) {
          assert.ok(data.msg.indexOf('请输入正确的edu邮箱')  > 0);
          assert.equal(helper.getValueOfXmlNode('ToUserName', data.msg), 'oNaHXsspWthiBKBda-E5C_OBP_ao');
          assert.equal(helper.getValueOfXmlNode('FromUserName', data.msg), 'gh_0c7058f90a8d');
          return Promise.resolve();
        });
      });
    });

    it('[processMessage.register.Nor.01].关注未注册, 存储email, 更新注册时间, 过期时间; 回复用户注册成功消息', function () {
      var spyEmit = sinonSandbox.spy(util.sandbox, 'emit');

      // 数据准备
      var newRegisterData = registerData.replace('oNaHXsspWthiBKBda-E5C_OBP_ao', 'oCxVxs7eTWNcwP2QyGqlDXyZpbVE');
      // 注册者
      var registrant = {
            "openid": "oCxVxs7eTWNcwP2QyGqlDXyZpbVE",
            "reference_code": util.generatePromotionCode('oNaHXsspWthiBKBda-E5C_OBP_ao'),
            "invalid_flag": 0,
            "user_type": 0,
            "point": 0,
            "due_date": 1451714959096,
            "register_date": 0,
            "qrcode": "https:\/\/mp.weixin.qq.com\/cgi-bin\/showqrcode?ticket=gQH47joAAAAAAAAAASxodHRwOi8vd2VpeGluLnFxLmNvbS9xL2taZ2Z3TVRtNzJXV1Brb3ZhYmJJAAIEZ23sUwMEmm3sUw==",
            "promotion_code": util.generatePromotionCode('oCxVxs7eTWNcwP2QyGqlDXyZpbVE'),
            "subscribe_time": 1449008888000,
            "headimgurl": "http:\/\/wx.qlogo.cn\/mmopen\/7y4xnsv09YZOoic8yb10tqx5rnflfUEFFbMD44F8yqGrDIcAutL4hucrDLeXCN98QQFPkyuNWgj4CpYMMwsygMnJDuplOjF5u\/0",
            "country": "中国",
            "province": "辽宁",
            "city": "大连",
            "language": "zh_CN",
            "sex": 1,
            "nickname": "曾大点",
            "subscribe": 1,
            "email": ""
          },

          // 推荐者
          reference = {
            "openid": "oNaHXsspWthiBKBda-E5C_OBP_ao",
            "reference_code": "",
            "invalid_flag": 0,
            "user_type": 0,
            "point": 0,
            "due_date": 1451714959096,
            "register_date": 0,
            "qrcode": "https:\/\/mp.weixin.qq.com\/cgi-bin\/showqrcode?ticket=gQH47joAAAAAAAAAASxodHRwOi8vd2VpeGluLnFxLmNvbS9xL2taZ2Z3TVRtNzJXV1Brb3ZhYmJJAAIEZ23sUwMEmm3sUw==",
            "promotion_code": util.generatePromotionCode('oNaHXsspWthiBKBda-E5C_OBP_ao'),
            "subscribe_time": 1449009999000,
            "headimgurl": "http:\/\/wx.qlogo.cn\/mmopen\/7y4xnsv09YZOoic8yb10tqx5rnflfUEFFbMD44F8yqGrDIcAutL4hucrDLeXCN98QQFPkyuNWgj4CpYMMwsygMnJDuplOjF5u\/0",
            "country": "中国",
            "province": "辽宁",
            "city": "大连",
            "language": "zh_CN",
            "sex": 1,
            "nickname": "曾健",
            "subscribe": 1,
            "email": ""
          };
      return Promise.all([weixinDao.findOneAndUpdate({openid: registrant.openid}, registrant),
        weixinDao.findOneAndUpdate({openid: reference.openid}, reference)])
        .then(weixin.processMessage.bind(weixin, newRegisterData))
        .then(function(data) {
          assert.ok(data.msg.indexOf('恭喜') > 0);
          assert.equal(helper.getValueOfXmlNode('ToUserName', data.msg), helper.getValueOfXmlNode('FromUserName', newRegisterData));
          assert.equal(helper.getValueOfXmlNode('FromUserName', data.msg), helper.getValueOfXmlNode('ToUserName', newRegisterData));
          return Promise.resolve(weixinDao.findByOpenId(registrant.openid)).then(function(user) {
            assert.equal(user.email, testingEmail);
            //assert.equal(user.due_date, user.register_date + parseInt(config.free_duration));
            assert.equal(user.subscribe_time, registrant.subscribe_time, '注册时不应该改变关注时间');
            assert.ok(0 <= Date.now() - user.register_date < 1000);
            return Promise.resolve();
          }).delay(200).then(function () {

            // 发5条消息
            assert.equal(spyEmit.callCount, 5);

            // 1. point奖励
            assert.equal(spyEmit.firstCall.args[0], util.sandbox.MSG.AWARD_REGISTER_QRCODE);
            assert.equal(spyEmit.firstCall.args[1].trigger.openid, registrant.openid);
            assert.equal(spyEmit.firstCall.args[1].referenceCode, registrant.reference_code);
            assert.equal(spyEmit.firstCall.args[1].actionType, 'register');

            // 2. 发确认邮件给用户
            assert.equal(spyEmit.secondCall.args[0], util.sandbox.MSG.MAIL_VERIFICATION);
            assert.equal(spyEmit.secondCall.args[1].openid, registrant.openid);

            // 3. 写入log
            assert.equal(spyEmit.thirdCall.args[0], util.sandbox.MSG.LOG_POINTHISTORY);
            assert.equal(spyEmit.thirdCall.args[1].triggered_by, registrant.openid);
            assert.equal(spyEmit.thirdCall.args[1].awarded_to, reference.openid);
            assert.equal(spyEmit.thirdCall.args[1].action_type, 'register');
            assert.equal(spyEmit.thirdCall.args[1].point, util.config.award_register_qrcode);

            // 4. 自动兑换point为vpn时间
            assert.equal(spyEmit.getCall(3).args[0], util.sandbox.MSG.EXCH_POINT_TO_VPN_TIME);

            // 5. 写入log
            assert.equal(spyEmit.getCall(4).args[0], util.sandbox.MSG.LOG_POINTHISTORY);
            assert.equal(spyEmit.getCall(4).args[1].action_type, 'exchange');
            assert.equal(spyEmit.getCall(4).args[1].triggered_by, reference.openid);
            assert.equal(spyEmit.getCall(4).args[1].point, -1 * util.config.award_register_qrcode);
            assert.equal(spyEmit.getCall(4).args[1].exchange.exchange_type, util.sandbox.MSG.EXCH_POINT_TO_VPN_TIME);
          });
        }, function(err) {
          return Promise.reject(err);
        });
    });

    it('[processMessage.register.Nor.02].关注已注册, 提示用户已经是注册用户了', function () {

      // 先关注
      return weixin.processMessage(subscribeData).then(function() {

        // 注册
        return weixin.processMessage(registerData).then(function() {

          return weixinDao.upsert({openid:'oNaHXsspWthiBKBda-E5C_OBP_ao'},{certificated:true}).then(function(){

            // 再次注册(使用同样的数据)
            return weixin.processMessage(registerData).then(function(data) {
              console.log("agiain====>"+JSON.stringify(data));
              assert.ok(data.msg.indexOf('您已经是注册用户了！')  > 0);
              assert.equal(helper.getValueOfXmlNode('ToUserName', data.msg), helper.getValueOfXmlNode('FromUserName', registerData));
              assert.equal(helper.getValueOfXmlNode('FromUserName', data.msg), helper.getValueOfXmlNode('ToUserName', registerData));
              return Promise.resolve();
            });
          });

        });
      })
    });

    it('[processMessage.register.Nor.03].关注未注册, 但email已经被别人注册, 提示用户使用其他mail完成注册', function () {

      // 先关注
      user_info.email = testingEmail;
      user_info.openid = 'oNaHXsspWthiBKBda-E5C_OBP_bo';
      user_info.certificated = true;
      return weixin.processMessage(subscribeData)
        .then(weixinDao.upsert(user_info, user_info))
        .then(function() {

          // 注册
          return weixin.processMessage(registerData).then(function(data) {

            assert.ok(data.msg.indexOf('您输入的邮箱已经被注册，请使用其他邮箱完成注册')  > 0);
            assert.equal(helper.getValueOfXmlNode('ToUserName', data.msg), 'oNaHXsspWthiBKBda-E5C_OBP_ao');
            assert.equal(helper.getValueOfXmlNode('FromUserName', data.msg), 'gh_0c7058f90a8d');
            return Promise.resolve();
          });
        })
    });
  });
});